﻿using System;
using System.Diagnostics;
using System.Linq;
using LINQPad.Extensibility.DataContext;
using LINQPad.OpenAccess.Helpers;
using LINQPad.OpenAccess.Settings;

namespace LINQPad.OpenAccess.Services.Builders
{
    /// <summary>
    /// <see cref="IConnectionStringBuilder"/>.
    /// </summary>
    public class PredefinedConnectionStringBuilder : IConnectionStringBuilder
    {
        #region Private

        #region - Vars

        [DebuggerBrowsable( DebuggerBrowsableState.Never )]
        private readonly IApplicationConfigService _appConfigService;

        #endregion

        #endregion

        #region Public

        #region - Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="PredefinedConnectionStringBuilder" /> class.
        /// </summary>
        /// <param name="appConfigService">The app config service.</param>
        public PredefinedConnectionStringBuilder( IApplicationConfigService appConfigService )
        {
            Guard.ArgumentNotNull( appConfigService, "appConfigService" );
            this._appConfigService = appConfigService;
        }

        #endregion

        #endregion

        #region IConnectionStringBuilder Members

        /// <summary>
        /// Determines whether the instance is able to build the connection for the specified parameters.
        /// </summary>
        /// <param name="providerName">Name of the provider.</param>
        /// <param name="connectionSettings">The connection settings.</param>
        /// <returns>
        ///   <c>true</c> if the instance is able to build the connection string; otherwise, <c>false</c>.
        /// </returns>
        public bool CanBuildConnectionStringFor( string providerName, ConnectionSettings connectionSettings = null )
        {
            return connectionSettings is PredefinedConnectionSettings;
        }
        /// <summary>
        /// Builds the connection string for the specified parameters.
        /// </summary>
        /// <param name="providerName">Name of the provider.</param>
        /// <param name="connectionInfo">The connection info.</param>
        /// <param name="connectionSettings">The connection settings.</param>
        /// <returns>
        /// The connection string.
        /// </returns>
        /// <exception cref="System.NotSupportedException">If the connection settings are not supported</exception>
        public string BuildConnectionString( string providerName, IConnectionInfo connectionInfo, ConnectionSettings connectionSettings )
        {
            Guard.ArgumentNotNullOrEmpty( providerName, "providerName" );
            Guard.ArgumentNotNull( connectionInfo, "connectionInfo" );
            Guard.ArgumentNotNull( connectionSettings, "connectionSettings" );

            var predefinedSettings = connectionSettings as PredefinedConnectionSettings;
            if ( predefinedSettings == null )
                throw new NotSupportedException
                        (
                            String.Format
                                    (
                                        "The connection settings type '{0}' is not supported by this builder instance ! (Type: {1})"
                                        , connectionSettings.GetType( ).FullName
                                        , this.GetType( ).FullName
                                    )
                        );
            if ( String.IsNullOrWhiteSpace( predefinedSettings.ConnectionName ) )
                throw new InvalidOperationException( "The predefined settings have no connection name set !" );
            if ( String.IsNullOrWhiteSpace( connectionInfo.AppConfigPath ) )
                throw new InvalidOperationException( "No application config file configured/specified !" );

            var predefinedConnections = this._appConfigService.GetPredefinedConnections( connectionInfo.AppConfigPath );
            if ( predefinedConnections == null || predefinedConnections.Length == 0 )
                throw new InvalidOperationException( String.Format( "The application config file doesn't contain any predefined connection ! (File: {0})", connectionInfo.AppConfigPath ) );
            var predefinedConnection = predefinedConnections.FirstOrDefault( p => predefinedSettings.ConnectionName.Equals( p.Name ) );
            if ( predefinedConnection == null )
                throw new InvalidOperationException
                    (
                        String.Format
                                (
                                    "The application config file contains no predefined connection named '{0}' doesn't contain any predefined connection ! (File: {1})"
                                    , predefinedSettings.ConnectionName
                                    , connectionInfo.AppConfigPath
                                )
                    );
            else if ( !String.IsNullOrWhiteSpace( predefinedConnection.ProviderName )
                        && !providerName.Equals( predefinedConnection.ProviderName ) )
                throw new InvalidOperationException
                        (
                            String.Format
                                    (
                                        "The requested provider '{0}' differs from the one provided by the application config '{1}' !"
                                        , providerName
                                        , predefinedConnection.ProviderName
                                    )
                        );
            return predefinedConnection.ConnectionString;
        }

        #endregion
    }
}
